#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <pthread.h>
#include <sys/types.h>
#include <limits.h> 
#include <errno.h>
#include <unistd.h>

////////blok definicji////////////////
#define LOOPS 4
#define number_of_threads1 4
#define number_of_threads2 8
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutex_t mutex_i = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutex_t mutex_j = PTHREAD_MUTEX_INITIALIZER; 


/////////zmienne globalne/////////////

unsigned int non_zeros;	//LICZBA NIEZEROWYCH ELEMENTÓW
unsigned int rowsPLUS1;	//ROZMIAR WEKTORA Z INDEKSAMI POCZĄTKÓW WIERSZY
unsigned int liczbaWierszy, liczbaKolumn, liczbaElementow =0;	//STAŁE OPISUJACE PARAMETRY MACIERZY
int a=1;
int postep1=0;
int postep2=0;
int id_watku;
int start,stop;
WINDOW *my_win;
FILE *plik;

WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);


typedef struct _Arg{
	unsigned int number_of_rows;
	unsigned int number_of_non_zeros;
	double *non_zeros;
	int *row_pointer;
	int *column_index;
	double *source_vector;
	double *destination_vector;
	int row_start;
	int row_stop;
	int number_of_threads;
	int pozycja;
	int suma;
}Argument;


///////////////funkcja mnozaca macierz przez wektor przystosowana do pracy z watkami
void *smvm_for(void *p)
{
	Argument* param= (Argument*)(p);
	
	for(param->pozycja=param->row_start;param->pozycja<=param->row_stop; ){
		postep2=param->pozycja;
		pthread_mutex_lock( &mutex_i );
		param->suma++;
		id_watku=pthread_self();
		//printf("\n\rwatek: %d, i: %d",id_watku,param->pozycja );
    	start=param->row_start;
    	stop=param->row_stop;
    	param->pozycja++;
		pthread_mutex_unlock( &mutex_i );
		
		for ( int j = param->row_pointer[param->pozycja]; j < param->row_pointer[param->pozycja+1]; j++ ){
			//pthread_mutex_lock( &mutex1 );
			param->destination_vector[param->pozycja] += param->non_zeros[j]*param->source_vector[param->column_index[j]];
			//   postep2++;
			//    pthread_mutex_unlock( &mutex1 );
        }
        usleep(850);
        	
	}
       	pthread_exit(NULL);
       return 0;
}

///////////standardowa funkcja mnozaca macierz w formacie CSR przez wektor///////////////
void smvm ( int *row_ptr, int* col_idx, double* value, double* x,double* y, int m) 
{  for ( int i=0; i <= m; i++ ){
        y[i] = 0;
        for ( int j = row_ptr[i]; j < row_ptr[i+1]; j++ ){
            y[i] += value[j]*x[col_idx[j]];
        }
    }
}


//////////funkcja wypisujaca wektor na ekran///

void wypiszVec( double * wek, int rozm)
{
	for (unsigned int i = 0; i < rozm; i ++)
	{
		printf("\n\rwatek: %lf" ,wek[i]);
	}
}


void* watek(void* _arg) {    
  
	while(postep2<6){
	//postep2++;
	sleep(1);
}

 return NULL;
}

void* watek2(void* _arg) {      

	
	sleep(1);
 return NULL;
}

/////////funkcja zerujaca wektor////////////
void init_wynik(double *wek, int liczbaKolumn)
{
		for(int k=0;k<liczbaKolumn;k++)
		{
			wek[k]=0;
		}
}

/////////funkcja ustawiajaca wektor//////////
void ustaw_wektor(double *vek, int count, double wartosc)
{
	for (unsigned int i = 0; i < count; i += 1)
	{
		vek[i]=wartosc;
	}
}

////////funkcja wczytujaca macierz w form CSR/////////

void get_matrix ( double * val, int * row_ptr , int * col )
{
	int s=0;
	int tmp,tmp2;
	
	//plik=fopen(str, "rb" );
	if  ((plik=fopen("raefskyCSR.txt", "r"))==NULL)
	{
 		printf("Nie moge otworzyc pliku!\n");   
	}
	fscanf(plik, "%d %d", &liczbaWierszy, &liczbaElementow);

	liczbaKolumn=liczbaWierszy;
	printf("\n%d %d %d", liczbaWierszy,liczbaKolumn,liczbaElementow);

	col=new int[liczbaElementow];
	row_ptr=new int[liczbaWierszy+1];
	val=new double[liczbaElementow];


	while (feof(plik)==0 && s<liczbaElementow)
	{
		fscanf(plik, "%d %d %lf", &tmp,&tmp2,&val[s]);
		col[s]=tmp2-1;
		
		if (s<liczbaWierszy+1)
		{ 
			row_ptr[s]=tmp-1;
		}
	    s++;
	}
	if  (fclose(plik))
	{
		printf("Nie moge zamknac pliku!\n");
	}

	//for(j=0;j<10;j++){
	//	printf("\n%d %d %.10lf", row_ptr[j],col[j],val[j]);
	//}
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////MAIN////////////////////////////////////////////////

int main(){

	/////tablice opisujace macierz////////
	double * values;
	int * row_ptr;
	int * col;
	/////////////////////////////////////
  
	int y;
	int ch;

	int startx, starty, width, height;
	int i, j,r,s=0;
	char str[80];
	
	////deklaracja wektorów wynikowych////////////
	double * wynik_1, wynik_2, wynik_3, wynik_4;
	
	////deklaracja wektora mnozonego////////
	double * vec;
	
	pthread_t id1;
	pthread_t id2;
    Argument arg1,arg2;
    Argument *arg;
       
    initscr();        
		
           //  errno = pthread_create(&id2, NULL, watek2, NULL);     
    		//test_errno("Nie powiodło się pthread_create");     
		//test_errno("Nie powiodło się pthread_create");
	


	raw();
	keypad(stdscr, TRUE);           /* We get F1, F2 etc..          */
                            
        height = LINES;
        width = COLS;
        starty = 0;  /* Calculating for a center placement */
        startx = 0;  
	printw("Press F1 to exit");
	
        refresh();
        my_win = create_newwin(height, width, starty, startx);
	move(2,4); attron(A_BOLD);
        wprintw(stdscr, "Mnozenie macierzy przez wektor"); 
	move(3,4); attron(A_BOLD);
        wprintw(stdscr, "pthreads/ncurses");  
	
	for (j=2;j<5;j++)
	for (i=COLS-25;i<COLS-22;i++){
	move(j,i); attron(A_BOLD|A_BLINK);
        wprintw(stdscr, "#");  }
	move(3,COLS-21); attron(A_BOLD|A_BLINK);
        wprintw(stdscr, "o");
	for (j=2;j<5;j++){
	move(j,COLS-19); attron(A_BOLD|A_BLINK);
        wprintw(stdscr, "#");  }

	my_win = create_newwin(height-7, width-2, 6, startx+1);

			move(8,4); attron(A_BOLD);
      			wprintw(stdscr, "Podaj nazwe macierzy:");  
 			getstr(str);
			move(8,45); attron(A_BOLD);
      			wprintw(stdscr, str);  
			move(10,4); attron(A_BOLD);
      			wprintw(stdscr, "Watek 1:");  
			move(11,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   0 procent"); 
			my_win = create_newwin(3, width-4, 12, startx+2);
				r=8;
			move(9+r,4); attron(A_BOLD);
      			wprintw(stdscr, "Watek 2:");  
			move(10+r,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   0 procent"); 
			my_win = create_newwin(3, width-4, 11+r, startx+2);
			
			
	/////////////Wczytanie macierzy//////////////
	//get_matrix(values,row_ptr,col);
	
	
	/////////Utworzenei wektorów wynikowych///////////////
	vec=new double[liczbaKolumn];
	/////////////////////////////////////////////////////

	double *wynik_watek=new double[liczbaKolumn];
	double *wynik_std=new double[liczbaKolumn];
	    	
	////////wypełnienie wekora mnozonego jedynkami/////
	ustaw_wektor(vec,liczbaKolumn,1);
				
				
				arg=new Argument[number_of_threads1];
				pthread_t *threads= new pthread_t[number_of_threads1];
				
				int dlugosc = (int)(((float)liczbaWierszy/(float)number_of_threads1)+0.5);
				
				//smvm(row_ptr, col, values, vec, wynik_std, liczbaKolumn);
				//smvm(row_ptr, col, val, vec, wynik_watek, liczbaKolumn);
				arg[0].number_of_rows=liczbaWierszy;
				arg[0].number_of_non_zeros=liczbaElementow;
				arg[0].non_zeros=values;
				arg[0].row_pointer=row_ptr;
				arg[0].column_index=col;
				arg[0].source_vector=vec;
				arg[0].destination_vector=wynik_watek;
				arg[0].row_start=0;
				arg[0].row_stop=dlugosc;
				arg[0].pozycja=0;

				for(int i=0;i<number_of_threads1; i++){
					
					arg[i]=arg[0];
					arg[i].row_start=dlugosc*i;
					//arg[i].row_stop=dlugosc*(i+1);
					if(i==(arg1.number_of_threads-1)){
						arg[i].row_stop=liczbaWierszy-1;
					}
					else{
						arg[i].row_stop=dlugosc*(i+1);
					}
					//spthread_create(&threads[i], NULL, smvm_for, (void*)&arg[i]);
			 	printf( "\n\rrow_start: %d, row_stop: %d,dlug: %d",arg[i].row_start,arg[i].row_stop,dlugosc); 
				}	
				
		
			
				
				//Argument* arg= (Argument*)(pa);
				
				/*int dlugosc = (int)(((float)arg1.number_of_rows/(float)arg1.number_of_threads)+0.5);
				pthread_t *threads= new pthread_t[arg1.number_of_threads];
				for(int i=0;i<arg1.number_of_threads; i++){
		
					//arg1.row_1=0;//dlugosc*i;
					arg1.i=dlugosc*i;
					if(i==(arg1.number_of_threads-1)){
					arg1.row_stop=arg1.number_of_rows-1;
					}
					else{
					arg1.row_stop=dlugosc*(i+1);
					}
					printf("start: %d, stop: %d",arg1.i,arg1.row_stop);
					pthread_create(&threads[i], NULL, smvm_for, (void*)&arg1);
					//postep2++;
					//sleep(1);
				}
			
				*/
				
				
			
						//errno = pthread_create(&id1, NULL, smvm_threads, (void*)&arg1);	
				//errno = pthread_create(&id1, NULL, watek, NULL);
				
				 	


	//////
	

			while(arg2.pozycja!=liczbaWierszy){
			move(10+r,4); attron(A_BOLD);
  	  	//printf( "\n\rwatek_pid: %d , i: %d, row_start: %d, row_stop: %d,dlug: %d",id_watku,postep2,start,stop,dlugosc); 
   		//printf( "\n\rSuma1: %d, Suma2: %d",arg1.suma, arg2.suma);
			move(12+r,postep2+2); attron(A_BOLD);
      			wprintw(stdscr, "|");
			refresh();
			usleep(1000);			
			}
			/*
				for (int i=0;i<liczbaKolumn;i++){
				if(wynik_std[i]!=wynik_watek[i])
				printf("\n\rBlad, %f.9 %f.9",wynik_std[i],wynik_watek[i] );
				printf("\n\rwynik: %f", arg1.destination_vector[i]);
				}
				printf("Sukces");

*/
        getch();     
	endwin();
	//errno = pthread_join(id1, NULL); 
             
//errno = pthread_join(id2, NULL);

return 0;
};
WINDOW *create_newwin(int height, int width, int starty, int startx)
{       WINDOW *local_win;

        local_win = newwin(height, width, starty, startx);
        box(local_win, 0 , 0);        
        
 
        wrefresh(local_win);           
        return local_win;
}



